CelinQ Insights · No. 46
Rename, Move or Both? Model-Aware Merge Rules for Enterprise Architect
Two architects touch the same element while apart. Whether that is a conflict at all depends on exactly what each of them changed.
Picture an element called Payment Gateway Adapter, sitting in a package called Integration > Legacy. One architect, working from home on a Monday, decides the name is misleading now that the adapter also handles refunds, and renames it to Payment Gateway Service. At almost the same moment, a colleague in the office is doing a quiet piece of housekeeping: the Legacy package is being retired, and its live contents are being relocated into Integration > Platform. Neither architect knows about the other's change. Both save. Both changes eventually reach CelinQ. The question that decides whether this is a routine synchronisation or a conflict that needs a person's attention is not "did two people touch the same element" — they plainly did — but something narrower and more useful: did they touch the same part of it.
This is the question this article answers in detail, because it is the one that separates a merge engine people trust from one they learn to double-check. A rename and a move, done by two different people to the same element at the same time, feel to a casual observer like exactly the kind of thing that should require a decision. In CelinQ's Fusion engine, they do not, and the reason why is worth walking through carefully, because the same reasoning explains a whole family of situations that look alarming on the surface and are actually harmless underneath.
The naive view: one element, one conflict
The easiest way to build a merge tool, and the way a great many tools quietly default to, is to treat "the element changed" as the unit of conflict. Under that model, if two people touch the same element while apart, you have a conflict, full stop, and someone has to look at it. It is easy to implement and it is never wrong in the narrow sense that it never merges something it shouldn't — but it is wrong in the sense that matters to a working team, because it manufactures far more conflicts than the situation actually contains. A rename and an unrelated status update on the same element get treated exactly like two people rewriting the same paragraph of a specification, when they are nothing alike. One team member spends their afternoon rubber-stamping a queue of "conflicts" that were never really in tension, and after a few weeks of that, people stop trusting the queue and start rubber-stamping it without reading it, which defeats the entire purpose of having one.
CelinQ starts from a different unit of analysis, one covered in more depth in the earlier piece on why an element-level conflict is usually too large. Every element decomposes into a set of independently trackable facts, or merge atoms: the scalar fields such as Name, Type, Stereotype, Notes and Alias; the tree parent, meaning which package the element lives in; the endpoints of a connector; each individual key in a tagged-value map; and, for a diagram object, its geometry as a single composite transform. Two changes only collide if they touch the same atom. A rename touches the Name atom. A move touches the tree-parent atom. Those are different atoms, on the same element, and the Fusion engine can see that plainly because it is not comparing "the element" as a lump — it is comparing the specific field each side actually wrote to.
Rename plus move: the same operation from two angles
Back to Payment Gateway Adapter. When both changes reach the canonical workspace, Fusion classifies the element as concurrently modified — its base version has moved since either side started — and runs a three-way merge from the recorded base. It finds that the Name atom differs from base only on the rename side, and the parent atom differs from base only on the move side. Neither side touched the field the other side changed. This is exactly the situation the rule TREE_MOVE_PLUS_RENAME exists to recognise, and it resolves automatically: the element ends up in Integration > Platform, carrying the name Payment Gateway Service, and both architects' intentions survive intact. Nobody has to choose between the rename and the move, because there was never a real choice to make. The two changes were never in competition; they only looked that way because they landed on the same row in a database.
It is worth sitting with why this feels surprising the first time you see it. Most people's mental model of a conflict comes from experience with document editing, where two edits to the same paragraph really do compete, because a paragraph is a single linear string and there is no way to represent "both people's version" at once without picking an order and hoping it reads sensibly. A model element is not like that. Its name and its location in the containment tree are genuinely independent properties, stored and versioned independently, and a merge tool that respects that independence can do something a text-diff tool structurally cannot: combine two edits that would look, on the surface, like they are fighting over the same thing.
Rename plus status update: the ordinary case that never becomes news
A more mundane but far more common pairing is a rename on one side and a lifecycle status change on the other — someone corrects a label while someone else marks the same element as Deprecated ahead of a migration. This is the plainest instance of the rule DISJOINT_PROPERTY_WRITES: two different scalar fields, written by two different people, from the same base. Because the fields are disjoint, the merged result simply contains both writes. Nothing about this situation is architecturally interesting, and that is precisely the point — the overwhelming majority of real-world concurrent edits inside a healthy team are exactly this shape, two people doing two unrelated, small, sensible things to the same element in the same week, and the correct behaviour is for the tool to be quiet about it. A merge engine earns trust less through the clever cases and more through never bothering anyone with the boring ones.
Move plus tagged-value update
Tagged values complicate the picture slightly, because a tagged-value collection is itself a small map rather than a single scalar, and the atom that matters is the individual key, not the collection as a whole. Suppose the architect doing the reorganisation also moves a second element, while a colleague — unaware of the reshuffle — adds a new tagged value to record its data-classification level. Two atoms are in play: the tree-parent atom (touched by the move) and one key of the tagged-value map (touched by the classification tag). They are different atoms on the same element, which is exactly the pattern TREE_MOVE_PLUS_PROPERTY_UPDATE is built to resolve, and the merge proceeds automatically, landing the element in its new package with the new tag attached.
The tagged-value map has its own internal version of the same principle. If two colleagues, unaware of each other, add different keys to the same element's tagged values — one recording a cost centre, the other recording a data-classification level — the rule MAP_DISTINCT_KEYS merges both additions, because distinct keys in a map are, definitionally, not the same fact. The boundary of that rule is precise and worth stating plainly: it only fires when the keys differ. Two people setting the same key to two different values is a genuine disagreement about what that key should say, and the map's internal three-way merge treats it accordingly, raising a capsule rather than guessing which value the workspace should keep.
Move plus connector creation
Connectors introduce a slightly different wrinkle, because a connector is its own entity with its own endpoints, not a property nested inside the elements it joins. If one architect moves Payment Gateway Service into a new package while another, working from an older view of the model, draws a new dependency connector from a reporting component into that same element, there is no collision at all in the strict sense: the move touches the element's parent atom, and the new connector is a separate entity being created, referencing the element by its stable identity rather than by its location. Because EA element references travel by identity, not by package path, the new connector resolves correctly to the moved element regardless of which package it now lives in. Structural reorganisation and relationship-building can proceed on entirely independent timelines without either side needing to know what the other did first.
Where a genuine collision can arise is if both people are changing the same connector's endpoint at once — one redirecting it to a different target while another redirects it to a different source, from the same base. That is a topology decision with no safe default, and it raises a TopologyConflict capsule rather than guessing which redirection the team actually intended. The distinction is deliberate: creating something new alongside a move is almost always safe to combine automatically, while two competing edits to one connector's actual wiring is a decision only a person should make.
Move plus move: the one that genuinely needs a person
Not every combination resolves itself, and it would be dishonest to imply otherwise. If two architects, from the same base, both move the same element — one into Integration > Platform, the other into Integration > Deprecated — the tree-parent atom has two different, mutually exclusive values, and there is no rule that can combine "put it here" with "put it there" into a sensible middle ground. This raises a StructuralConflict capsule, deliberately, and the workspace's canonical state holds at the last known-good position until a person decides where the element actually belongs. This is worth contrasting directly with the rename-plus-move case above, because the difference between an automatic merge and a manual decision is not about how dramatic the change looks. A rename and a move look, superficially, like a bigger combined change than two competing moves — and yet the first resolves itself instantly while the second stops and asks. The determining factor is always whether the same atom received two different values, never how significant either change appears from the outside.
The test a merge rule applies is never "how big is this change." It is "did two people write different values to the exact same fact." Everything else is noise the model doesn't need a human to filter.
Stable identity versus path identity
None of the rules above would work if CelinQ tracked elements by where they currently sit in the package tree rather than by a durable internal identity. A system built on path identity — treating Integration/Legacy/Payment Gateway Adapter as the "name" of the thing — has no coherent way to represent a rename and a move happening at once, because the path itself is the union of both facts, and changing either one changes the path. Two edits that changed different fields would look, to a path-identity system, like two edits that both changed the same string, and the false-conflict rate would climb accordingly. Worse, an element that is deleted from one path and recreated at another looks structurally identical to an element that has genuinely been renamed and relocated, which is exactly the ambiguity that the resurrection problem exists to prevent.
CelinQ instead assigns every entity a stable identity at the moment it is first synchronised, and every subsequent operation — the rename, the move, the tag, the connector — refers back to that identity rather than to a location. The package tree is just one more property of the entity, versioned the same way its name and its tagged values are, rather than the address the entity is filed under. This is what allows the tree-parent atom to be diffed on its own, cleanly, independent of everything else about the element, and it is the structural reason the rename-plus-move case above is not merely a clever special case but a direct consequence of how identity is modelled from the ground up.
Commutative operations, and why some things refuse to commute
There is a useful way to think about why some combinations merge automatically and others do not, borrowed loosely from the idea of commuting operations in concurrent systems: two changes can be combined safely if the order you apply them in does not matter to the outcome. Renaming then moving an element produces the identical final state to moving then renaming it — the two operations commute, because they act on independent atoms. Two competing moves do not commute at all; applying "send it to Platform" before "send it to Deprecated" produces a different final location than the reverse order, and a system that picked one order arbitrarily would be making a silent decision the architects never agreed to. The whole rule matrix in Fusion — covered from first principles in the piece on merging models rather than text — is, underneath the specific rule names, an exercise in identifying exactly which pairs of operations on a model genuinely commute and which do not, and never assuming commutativity where it has not actually been proven.
Diagrams follow the identical logic at a different scale. Two architects repositioning two different shapes on the same diagram touch independent geometry atoms and merge without incident, because the rule DIAGRAM_DISTINCT_OBJECT_GEOMETRY recognises that unrelated objects on a shared canvas are, structurally, no different from unrelated elements in a shared package. Even the same diagram object can commute with itself: if one person nudges a shape's position and another resizes it, the geometry decomposes into a translation and a size delta that compose cleanly into a single merged transform. Two people dragging the same shape to two different positions, on the other hand, do not commute, and that raises a GeometryConflict capsule — unless a workspace has explicitly opted into a last-writer policy for geometry, a deliberate convenience trade-off covered further in the piece on proof-carrying merges.
Rule-based resolution and why it can be audited
What makes this approach different from a merge tool that simply feels clever is that every automatic decision is rule-based rather than heuristic. Fusion is a pure function of the base state, the two divergent states, and the workspace's policy — no clock, no randomness, no "usually works" statistical guess. Given the same three inputs, it produces the same output every time, on every machine, which is what allows the engine to be exhaustively tested rather than merely trusted on faith. And every time an automatic merge fires, the specific rule that proved it safe is written into the revision alongside the change itself, visible afterwards in the Control Plane's Fusion view rather than disappearing the moment the merge completes. An architect reviewing last week's activity does not see "merged automatically" as an unexplained fact; they see TREE_MOVE_PLUS_RENAME or DISJOINT_PROPERTY_WRITES attached to the specific element it applied to, which turns "I have to trust the tool" into "I can check exactly what the tool decided and why." That distinction is the entire subject of the next article in this pair.
The old shared-repository answer to this problem, covered earlier in this series, was to prevent the situation from arising at all: lock the element, and nobody else can touch any part of it until you are done. Rule-based merging keeps the safety without the queue — but only because the rules are narrow enough to trust and honest enough to say "not this one" when they should.
A worked example, start to finish
It helps to walk the Payment Gateway Adapter scenario all the way through, because the abstract rule names can make the process sound more mysterious than it is. Both architects start the day from the same server revision, so both changes carry the identical BaseVersion when they eventually push. The home-based architect's change reaches the server first: a small ChangeSet containing a single scalar write, Name from Payment Gateway Adapter to Payment Gateway Service. Because the server's current head still matches the base the change was written against, this is a sequential apply — nothing to reconcile, no rule fires, it simply lands, and the element's version number advances by one.
An hour later the reorganisation lands: a ChangeSet moving the element into Integration > Platform, still declaring the original base version, because the colleague doing the move never refreshed after the rename went in. This time the server's head has moved past that base, so the classifier in FusionPipeline recognises genuine concurrency and hands the situation to the Fusion engine for a three-way comparison against the stored base, the current canonical state, and the incoming change. The engine finds one atom changed since the base — the parent — and finds that atom untouched by the intervening rename. There is nothing for the two changes to disagree about, so the rule TREE_MOVE_PLUS_RENAME fires, the merged state is computed, and a decision recording exactly that rule name is written alongside the new revision. Neither architect sees a dialog, a queue entry, or a decision to make. The next time each of their local Enterprise Architect repositories synchronise, they simply see an element that has both been renamed and relocated, because that is, in fact, what happened.
Now change one detail: suppose the colleague doing the reorganisation had, instead of only moving the element, also decided independently that it should be renamed to Legacy Payment Adapter as part of tidying up the retired package before the move. Both changes now touch the Name atom, from the same base, with two different values. The classifier still recognises concurrency, but this time the three-way comparison finds a real disagreement rather than a clean split of atoms, and it raises a ScalarConflict capsule instead of merging silently. Both proposed names, and the original base value, are preserved in the capsule for a person to look at. The distinction between this outcome and the first is not a matter of degree — it is not that this change was "bigger" — it is that this specific pair of changes wrote two different values to the identical fact, and that is the one situation no rule is permitted to paper over.
The honest limits
This rule-based approach earns its keep by being conservative in exactly the right places, and it is worth being explicit about where that conservatism shows up as friction rather than convenience. Move-plus-move on the same element always stops for a person, by design, because there genuinely is no safe automatic answer to two contradictory placements — and a team that reorganises the same corner of a large model in the same week, from two different starting points, should expect to see that capsule and should not expect CelinQ to guess its way past it. Concurrency today is also resolved pairwise against the current canonical head rather than as a simultaneous many-way comparison: if three people diverge from the same base and push in sequence, each push is merged against the latest state as it lands, one at a time, rather than all three being reconciled together in a single three-way-plus-one operation. Convergence and no-loss both still hold under that model — every push is checked against the true current state, not a stale one — but a workspace with unusually heavy simultaneous editing from many people on the same small area of a model will see more sequential capsules than a single combined view might ideally show, and that is a known shape of the current design rather than a hidden gap.
It is also worth being clear about what this rule matrix does not claim to be. It is not an opinion about architecture quality, and it does not know whether a rename was a good idea or whether an element belongs in Platform rather than Deprecated. It only knows, with certainty, whether two specific facts collided. That narrowness is deliberate and, in practice, is what makes the automatic cases trustworthy: a rule that only ever says "these two things definitely did not touch the same fact" has no room to be subtly wrong about something bigger. The rules earn automation by staying small enough to prove, and the moment a situation genuinely needs judgement rather than proof, the correct behaviour is to stop and ask, not to make a confident-sounding guess. That is exactly what happens for a move-plus-move, a same-key tagged-value clash, or a same-connector endpoint fight, and the next articles in this series look closely at two of the sharper edges of that boundary: what happens when one side of the collision is a deletion, and how the tool explains its own automatic decisions well enough for an enterprise audit to take them at face value.