less ~/notes/a-merge-is-only-safe.md

A merge is only safe when neither side is asserting a fact

24 Jul 2026· 2 min read
Node.jsPostgreSQL

The resolver checks rules in order, and the order is the design. Disjoint fields merge. A physical claim from the field wins. Rank comparison decides the rest, and an equal rank with overlapping fields escalates rather than guesses.

I put the merge rule first, and shipped it into a test suite that immediately went red. Trace it: the driver writes status and a delivery timestamp, the office writes an assignee. Those field sets do not overlap. So the merge rule fired, saw no disagreement, and applied both, producing a job that was simultaneously delivered and reassigned to a second driver. The parcel is with the customer and the system has just dispatched another van to collect it. That is precisely the failure the entire architecture exists to prevent, and I wrote it wrong.

Four unit tests caught it in seven tenths of a second. The fix is one condition: a physical claim skips the merge path unconditionally. The lesson generalises well beyond this codebase. A merge is only safe when neither side is asserting a fact about the world. Two edits to metadata can be blended. An edit that says this physically happened cannot be blended with anything, because it may invalidate the other side entirely, so it has to be arbitrated rather than combined.

That paragraph is now a comment above the check, so the next person who tries to simplify the ordering hits the reasoning before they hit the code.

The reason those tests existed at all is that the resolver has zero imports. No database, no logger, no clock; everything arrives as an argument. That is not architectural purity, it is a testing strategy with a measurable consequence: forty-eight tests in under a second, on the logic that decides whether a driver day of work is honoured. Had it reached for a database import, every one of those cases would need Postgres to run, they would take minutes, and I would have written ten instead of forty-eight. The bug would have been in the thirty-eight I did not write. Fast tests get written and slow tests get skipped, so the design has to make the important tests fast or they will not exist.