Two-way sync needs an authority

A ticket that moves on its own is an authority problem wearing the costume of a delivery bug. Two systems writing one field need a declared loser.

Two glass sync rails meet at a central prism glowing orange

A ticket that changes column while nobody on the team touched it reads as a bug in the integration, and it is usually the integration working. Two systems writing the same field both have the right to be right, so somebody loses every tie. A system that never named the loser picks one anyway, by arrival order.

Nobody on the team moved the ticket

The report reaches bug triage as a question about trust, not about a defect. A ticket sat in QA on Monday and sits in WIP on Tuesday, the engineer who owns it did not move it, and the trail names something nobody recognises. The instinct is to hunt for the fault in the delivery: a webhook that fired twice, a retry that replayed an old payload.

That instinct sends people to the wrong place. The delivery did its job, and both systems did what they were configured to do. A developer dragged the issue in the tracker at roughly the moment a support lead dragged the ticket on the board, and each then told the other. What is missing is a rule naming which of those two gestures survives, written down before either happened. Every integration that writes state in both directions has such a rule, and most hold it by accident, in the shape of whichever update was processed second.

Somebody has to lose, and deciding late is deciding by accident

There are three honest answers, and they are not equally good.

The first is that the most recent write wins. It sounds neutral, and it is what falls out of writing no rule at all. What it does instead is hand the decision to two clocks that were never synchronised and to a queue depth nobody controls, so a team gets one answer on a slow afternoon and the opposite one on a fast morning.

The second is to dissolve the conflict instead of resolving it, and let a convergent data structure settle it later. That works for a set of labels or a text buffer. A pipeline stage is one field with human meaning behind it: QA and Done do not merge, because two people decided two different things and no data structure turns a disagreement into an agreement.

The third is to name an authority in advance and let it win every tie. We name the tracker, and the reasoning is about where the work happens, not where the evidence lives. A feedback tool that overrules a tracker transition comes off the stack, and being right about one ticket is not worth that.

Picking has a price. The other side loses, and somebody has to be told.

A tie is narrower than two people disagreeing

The word conflict does more damage here than it should, because two edits landing close together are not automatically one. A tie has to satisfy both halves of a narrower test: the local value has drifted away from the last value both sides agreed on, and it is not already where the incoming value wants to put it. A support lead who moved a ticket to Done a moment before the tracker said Done lost nothing, and warning them otherwise trains a team to ignore the warning that matters.

Order needs the same narrowing. An event reporting an older change must never overwrite a newer one, and the comparison deciding which is older cannot be a clock on either machine. It is the tracker’s own record of when that issue last changed, carried forward on every apply, so an event that lost a race in the network arrives late and moves nothing.

The loop closes on two such facts rather than on identity: an outbound push is skipped when the status already matches what both sides last agreed on, and a status write that changes nothing records nothing: the row is touched, and no activity entry, no notification and no webhook follows it. Detecting your own updates by actor instead depends on every provider handing that identity back, and one that does not is an echo nobody catches until a ticket has bounced all afternoon.

Every tracker gets the same rule, and none of them agrees what a state is

The rule is identical across the seven trackers the adapter contract covers. The thing being ruled on is not, which is where a state map stops being configuration and becomes a design decision.

TrackerWhat it treats as a stateWhat that costs the map
LinearWorkflow states carrying a typeThe closest thing to a real state machine, so mapping is nearly a rename
JiraStatuses defined per project, pooled across issue typesThe map belongs to one project; a second project needs its own
GitHubNothing. An issue is open or closedIntermediate stages exist only as labels the app mints and owns; the repository’s own labels are left untouched
TrelloWhich list the card sits inA board with one Doing list cannot express two stages, so one stays unmapped
AsanaSections of a project, with completion tracked apart from themA task completed without changing section is a move the map cannot see
ClickUpStatus strings scoped to one list, where the identifier is the visible nameRenaming a status breaks the map, because the name was the identifier
monday.comGroups on a boardThe map is per board, so a second board needs its own

Set side by side, the state map stops looking like configuration. Three of these express a stage as the container a card sits in, so the pipeline has to fit inside a vocabulary somebody else designed for another purpose; a fourth has no vocabulary for an intermediate stage at all, which is why every intermediate stage becomes a label the app mints on issues in GitHub, and the last one is the issue being closed. A tie can only ever be about a stage both sides can name, and the right-hand column is the list of stages one side cannot.

An admin sets that map once for each connected tracker, and a map that is not one to one does not survive being saved: the inbound direction reads it backwards, so a state a second stage tries to claim is dropped, because one tracker state pointing at two stages has no unambiguous answer. A state arriving from the tracker that the map does not cover is recorded against the ticket and then ignored. The alternative is a guess, and a guess moves tickets nobody asked to move.

The board can disagree with its tracker, and it says so

So the trade is stated instead of buried. When two people move the same ticket on both boards inside one window, the tracker wins and the Notarus move is applied and then replaced. What becomes of the losing decision is the part worth knowing.

What becomes of the losing moveWhere it goes
The value the tracker replacedKept on the ticket’s activity record, and named in the chat message
The link between ticket and issueMarked in conflict, so the board card carries the marker
The teamA message on the project’s chat channel, when one is connected

None of those three is a prompt: nobody is asked to pick a winner afterwards, because the picking happened when the connection was configured.

The limit worth planning around is ours rather than the practice in general: status gets managed in one place, and that place is the tracker. A team treating the Notarus board as where a stage is decided will keep meeting the tracker at ties and losing them, correctly, and every loss is noise a stand-up absorbs. The board is where the evidence is read and the tracker is where the stage is agreed, which is the shape of every project management integration here.

One more thing decides how much any of this matters. Webhook delivery is the fast path and not the only one: every fifteen minutes a reconciliation pass re-reads a bounded batch of linked issues, least recently checked first, and applies whatever the tracker currently says. A board that drifted quietly gets repaired on that rota without anybody filing a second report, though a link already marked in conflict sits outside the rota and waits for the tracker’s next delivery instead. The guarantee is a rota, not a deadline, and the batch is drawn across the whole installation rather than one project, so the wait stretches with every link queued ahead of yours.

Common questions

What happens when a ticket and its linked issue are moved at the same time?

The tracker wins. The move made on the Notarus board is applied and then replaced by the tracker's, the replaced value is kept on the ticket's activity record and named in the chat message, and the link is marked as in conflict so the board card carries the marker. Nothing is discarded quietly.

Why should the tracker win a two-way sync conflict instead of the tool that captured the bug?

Because engineers spend their working day in the tracker, and a feedback tool that overrules a tracker transition is a tool that comes off the stack. The rule also has to be identical in every project, since a per-project answer would make the same gesture mean different things on different boards.

How does a two-way sync avoid an infinite echo loop?

Two independent facts have to hold at once. An outbound push is skipped when the new status already equals the last value both sides agreed on, and a status write that changes nothing records nothing. Detecting your own updates by actor identity is the fragile version, because it depends on every provider handing that identity back.

What happens when somebody moves an issue into a status that is not in the map?

Nothing moves. The unmapped state is recorded against the ticket as one the map does not cover, and the ticket keeps the stage it had. Guessing would be worse, because a wrong guess moves tickets nobody asked to move, which is the failure a hand-set map exists to prevent.

How long can a board disagree with its tracker if a webhook is lost?

Until a reconciliation pass reaches that link. The pass runs every fifteen minutes and re-reads a bounded batch of linked issues, least recently checked first, and that batch is drawn across every project on the installation rather than one, so the wait grows with the total number of links queued ahead of it. Webhook delivery is the fast path, and the poll is the one that never gets skipped.

The Notarus team

The people who build Notarus. We write up the decisions behind the product when the reasoning is worth more than the changelog line.

Keep reading

Reports with the evidence attached.

Start free, one script tag.

<script src="…/widget.js" data-key="pb_pub_…">